package com.googlecode.dgwt.client.dijit.view.store;

import java.util.List;

import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.EventTarget;
import com.google.gwt.dom.client.Node;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Event;
import com.googlecode.dgwt.client.dijit.Dijit;
import com.googlecode.dgwt.client.dijit.base.selection.Selection;
import com.googlecode.dgwt.client.dijit.event.RowClickEvent;
import com.googlecode.dgwt.client.dijit.event.RowClickHandler;
import com.googlecode.dgwt.client.dijit.event.RowDeselectEvent;
import com.googlecode.dgwt.client.dijit.event.RowDeselectHandler;
import com.googlecode.dgwt.client.dijit.event.RowDoubleClickEvent;
import com.googlecode.dgwt.client.dijit.event.RowDoubleClickHandler;
import com.googlecode.dgwt.client.dijit.event.RowSelectEvent;
import com.googlecode.dgwt.client.dijit.event.RowSelectHandler;
import com.googlecode.dgwt.client.dijit.theme.CSS;
import com.googlecode.dgwt.client.dijit.view.render.base.RowRender;
import com.googlecode.dgwt.client.dojo.Console;
import com.googlecode.dgwt.client.dojo.Dojo;
import com.googlecode.dgwt.client.dojo.data.base.IndexedStore;
import com.googlecode.dgwt.client.dojo.data.base.Store;
import com.googlecode.dgwt.client.dojo.data.listener.DataListener;
import com.googlecode.dgwt.client.dojo.exception.AttachException;

/**
 * Base class for views that works with a {@link IndexedStore}. <br/>
 * Provides general routines to render rows and to fire row events.
 * 
 * @author wellsantos@wellsantos.com
 * @since 0.01
 */
public abstract class RowViewBase extends StoreViewBase implements RowView, DataListener<Object> {

    private Selection selection;

    private RowRender render;
    

    public RowViewBase() {
        this(true);
    }

    protected RowViewBase(boolean ready) {
        super(false);
        
        this.render = createRender();
        this.selection = createSelection(10);
        
        if (ready){
            postCreate();
        }
    }

    protected abstract RowRender createRender();

    @Override
    protected void onPostCreate() {
        super.onPostCreate();
        this.sinkEvents(getSelection().getEventsToSink()
                | Event.ONBLUR | Event.ONFOCUS | Event.ONMOUSEDOWN);
    }

    @Override
    protected void buildRendering() {
        getRender().buildRendering();
    }
    
    @Override
    public CSS getThemeCSS() {
        return Dijit.getTheme().dijitIndexedView();
    }

    @Override
    public IndexedStore<Object> getStore() {
        return (IndexedStore<Object>) super.getStore();
    }

    protected RowRender getRender() {
        return render;
    }

    public Selection getSelection() {
        return selection;
    }

    public Element getFocusedRow() {
        int rowIndex = getFocusedRowIndex();
        if (rowIndex == -1){
            return null;
        }
        return getRow(rowIndex);
    }

    public int getFocusedRowIndex() {
        return getSelection().getFocused();
    }
    
    protected boolean hasScrollableElement(){
        return hasAttachedElement("scrollableElement");
    }

    protected Element getScrollableElement(){
        Element element = getAttachedElement("scrollableElement", null);
        return element == null ? getContainerElement() : element;
    }

    
    // Store Item/Row assignment
    
    
    public List<Element> getRows(){
        return getRender().getRows();
    }
    
    public Element getRow(int rowIndex){
        return getRender().getRow(rowIndex);
    }
    
    public int getRowIndex(Element rowElement){
        return getRender().getRowIndex(rowElement);
    }

    public Object getRowItem(int rowIndex){
        return getStore().get(rowIndex);
    }
        
    @Override
    public int getRowCount() {
        return getRender().getRowCount();
    }

    @Override
    public boolean isRowSelected(int rowIndex) {
        return getSelection().contains(rowIndex);
    }

    
    // Implements DataListener
    
    
    @Override
    protected void doAttach(Store<Object> store) throws AttachException {
        if (!(store instanceof IndexedStore<?>)){
            throw new AttachException(this, "the attached store should be a IndexedStore descendant");
        }
        super.doAttach(store);
        getRender().setRowCount(store.size());
    }

    @Override
    protected void doDetach(Store<Object> parent) {
        getRender().setRowCount(0);
        super.doDetach(parent);
    }

    @Override
    public void onAdd(int index, Object item) {
        getRender().addRow(index, item);
    }

    @Override
    public void onRemove(int index, Object item) {
        getRender().removeRow(index);
    }

    @Override
    public void onReplace(int rowIndex, Object oldItem, Object newItem) {
        getRender().updateRow(rowIndex, newItem);
    }

    @Override
    public void onRefresh() {
        getRender().updateRows();
    }

    @Override
    public void onRefreshItem(Object item) {
        getRender().updateRows(item);
    }

    @Override
    public void onRefreshValue(Object item, int columnIndex) {
        onRefreshItem(item);
    }
    
    @Override
    public void onModified(Object item) {
        // don't needs to update
    }

    @Override
    public void onFetch(int index, Object item) {
        // don't needs to update
    }


    // Event Handlers
    
    
    @Override
    public void onBrowserEvent(Event event) {
        int rowIndex = -1;
        int eventType = event.getTypeInt();
        
        switch (eventType) {
            case Event.ONCLICK:
                if ((rowIndex = getRowIndexForEvent(event)) != -1){
                    onRowClick(event, rowIndex);
                }
                break;
            case Event.ONDBLCLICK:
                if ((rowIndex = getRowIndexForEvent(event)) != -1){
                    onRowDoubleClick(event, rowIndex);
                }
                break;
            case Event.ONKEYDOWN:
                rowIndex = getFocusedRowIndex();
                if (rowIndex == -1){
                    rowIndex = 0;
                }
                onRowKeyDown(event, rowIndex);
                break;
            case Event.ONKEYPRESS:
                rowIndex = getFocusedRowIndex();
                if (rowIndex == -1){
                    rowIndex = 0;
                }
                onRowKeyPress(event, rowIndex);
                break;
            case Event.ONKEYUP:
                rowIndex = getFocusedRowIndex();
                if (rowIndex == -1){
                    rowIndex = 0;
                }
                onRowKeyUp(event, rowIndex);
                break;
            case Event.ONMOUSEDOWN:
                Console.log("MOUSEDOWN");
                break;
            case Event.ONFOCUS:                
                Console.log("FOCUS");

                if ((rowIndex = getFocusedRowIndex()) != -1){
                    onRowFocus(rowIndex);
                }
                break;
            case Event.ONBLUR:
                Console.log("BLUR");
                
                if ((rowIndex = getFocusedRowIndex()) != -1){
                    onRowBlur(rowIndex);
                }
                break;
        }
        
        if (rowIndex != -1 
                && (eventType & getSelection().getEventsToSink()) != 0){
            getSelection().onBrowserEvent(event, rowIndex);
        }
        
        super.onBrowserEvent(event);
    }

    protected int getRowIndexForEvent(Event event) {
        EventTarget target = event.getEventTarget();
        if (target != null){
            
            Element domElement = getElement();
            Node node = target.<Element>cast();
            
            do {
                int rowIndex = getRowIndex(node.<Element>cast());
                
                if (rowIndex != -1){
                    return rowIndex;
                }
                
                node = node.getParentNode();
                
            } while (node != domElement && node != null);           
        }
        return -1;
    }
    
    public HandlerRegistration addRowClickHandler(RowClickHandler handler) {
        sinkEvents(Event.ONCLICK);
        return addHandler(handler, RowClickEvent.getType());
    }

    public HandlerRegistration addRowDoubleClickHandler(RowDoubleClickHandler handler) {
        sinkEvents(Event.ONDBLCLICK);
        return addHandler(handler, RowDoubleClickEvent.getType());
    }

    protected void onRowClick(Event event, int rowIndex){
        RowClickEvent.fire(this, event, rowIndex, getRowItem(rowIndex));
    }
    
    protected void onRowDoubleClick(Event event, int rowIndex){
        RowDoubleClickEvent.fire(this, event, rowIndex, getRowItem(rowIndex));
    }
    
    protected void onRowMouseDown(Event event, int rowIndex){
        // TODO IndexedView:onBrowserEvent 
    }

    protected void onRowMouseMove(Event event, int rowIndex){
        // TODO IndexedView:onBrowserEvent 
    }

    protected void onRowMouseUp(Event event, int rowIndex){
        // TODO IndexedView:onBrowserEvent 
    }

    protected void onRowMouseOver(Event event, int rowIndex){
        // TODO IndexedView:onBrowserEvent 
    }

    protected void onRowMouseOut(Event event, int rowIndex){
        // TODO IndexedView:onBrowserEvent 
    }
    
    protected void onRowKeyDown(Event event, int rowIndex){
        // TODO IndexedView:onBrowserEvent 
    }

    protected void onRowKeyPress(Event event, int rowIndex){
        // TODO IndexedView:onBrowserEvent 
    }

    protected void onRowKeyUp(Event event, int rowIndex){
        // TODO IndexedView:onBrowserEvent 
    }


    // Selection Management
    

    protected Selection createSelection(int capacity) {
        return new Selection(capacity){

            @Override
            public int getMaxRange() {
                return getStore().size();
            }

            @Override
            public int getPageRange() {
                if (getStore().isEmpty()){
                    return 1;
                }
                int h = getRow(0).getOffsetHeight();
                if (h < 1){
                    return 1;
                }
                h = getScrollableElement().getOffsetHeight() / h;
                return h < 1 ? 1 : h;
            }
            
            @Override
            protected boolean onCanDeselect(int item) {
                return RowViewBase.this.onRowCanDeselect(item);
            }

            @Override
            protected boolean onCanSelect(int item) {
                return RowViewBase.this.onRowCanSelect(item);
            }

            @Override
            protected void onDeselect(int item) {
                RowViewBase.this.onRowDeselect(item);
            }

            @Override
            protected void onSelect(int item) {
                RowViewBase.this.onRowSelect(item);
            }

            @Override
            protected void onBlur(int item) {
                RowViewBase.this.onRowBlur(item);
            }

            @Override
            protected void onFocus(int item) {
                RowViewBase.this.setFocus();
                RowViewBase.this.onRowFocus(item);
            }
            
        };
    }
    
    public HandlerRegistration addRowSelectHandler(RowSelectHandler handler){
        return addHandler(handler, RowSelectEvent.getType());
    }
    
    public HandlerRegistration addRowDeselectHandler(RowDeselectHandler handler){
        return addHandler(handler, RowDeselectEvent.getType());
    }
    
    protected void onRowSelect(int rowIndex) {
        Dojo.addClass(getRow(rowIndex), Dijit.getTheme().dijitSelectedName());
        RowSelectEvent.fire(this, rowIndex, getRowItem(rowIndex));
    }

    protected void onRowDeselect(int rowIndex) {
        Dojo.removeClass(getRow(rowIndex), Dijit.getTheme().dijitSelectedName());
        RowDeselectEvent.fire(this, rowIndex, getRowItem(rowIndex));
    }

    protected boolean onRowCanSelect(int rowIndex) {
        // TODO IndexedView: fire event onCanSelect
        return true;
    }

    protected boolean onRowCanDeselect(int rowIndex) {
        // TODO IndexedView: fire event onCanDeselect
        return true;
    }

    protected void onRowFocus(int rowIndex) {
        Element rowElement = getRow(rowIndex);
        Dojo.addClass(rowElement, Dijit.getTheme().dijitFocusedName());
        if (hasScrollableElement()){
            Dojo.ensureVerticalVisibility(getScrollableElement(), rowElement, false);
        }
        // TODO IndexedView: fire event 
    }

    protected void onRowBlur(int rowIndex) {
        Dojo.removeClass(getRow(rowIndex), Dijit.getTheme().dijitFocusedName());
        // TODO IndexedView: fire event 
    }

}
